package com.whately.core.base.fileupload.qiniu;

import cn.hutool.core.collection.CollUtil;
import com.whately.core.base.constant.DebugSwitcher;
import com.whately.core.base.exception.ServiceException;
import com.whately.core.base.fileupload.constant.FileFormatConstant;
import com.whately.core.base.fileupload.constant.FileModeEnum;
import com.whately.core.base.fileupload.constant.FileSenderTypeEnum;
import com.whately.core.base.fileupload.constant.FileStoreTypeEnum;
import com.whately.core.base.fileupload.qiniu.conf.QiNiuSelfDefineConfigUploadFile;
import com.whately.core.base.fileupload.qiniu.constants.QiNiuZoneHtmlBucketEnum;
import com.whately.core.base.fileupload.qiniu.constants.QiNiuZonePrivateBucketEnum;
import com.whately.core.base.fileupload.qiniu.model.BucketModel;
import com.whately.core.base.fileupload.qiniu.model.QiNiuStorageUtil;
import com.whately.core.base.fileupload.qiniu.util.ImageInfoVo;
import com.whately.core.base.fileupload.util.FileUploadPathCreatorUtil;
import com.whately.core.base.fileupload.vo.FileSenderBO;
import com.whately.core.base.fileupload.vo.PathAndNameBo;
import com.whately.core.base.thumb.ImageUtils;
import com.whately.core.base.utils.DateUtil;
import com.whately.core.base.utils.FileUtils;
import com.whately.core.base.utils.UUIDUtils;
import org.apache.commons.io.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class QiNiuFileUploadProxy {
    private static final Logger logger = LoggerFactory.getLogger(QiNiuFileUploadProxy.class);


    @Autowired
    private DebugSwitcher debugSwitcher;

    @Autowired
    private Executor threadPoolTaskExecutor;

    @Autowired
    private QiNiuSelfDefineConfigUploadFile selfDefineConfigUploadFile;



    private final String html_starting = "<!DOCTYPE HTML>" +
            "<html>" +
            "<meta charset='UTF-8'>" +
            "<title></title>" +
            "<body>";
    private final String html_body_begin="<!--htmlBodyBegin-->";
    private final String html_body_end="<!--htmlBodyEnd-->";
    private final String html_ending="</body></html>";


    public ImageInfoVo createUploadFileBucketPathInfo(String bucket, MultipartFile multipartFile) {
        if (StringUtils.isBlank(bucket)) {
            throw new ServiceException(400,"Bucket无效");
        }

        ImageInfoVo  imageInfoVo =  createPathInfo(bucket,multipartFile.getOriginalFilename());
        //校验大小和格式
        //checkFileSizeAndSuffix(bucket,multipartFile,imageInfoVo.getDotAndSuffix());
        return imageInfoVo;
    }

    private ImageInfoVo createPathInfo(String bucket, String originalFilename){
        PathAndNameBo bo = FileUploadPathCreatorUtil.generateFilePathAndName(bucket,getFileModeEnum(bucket),originalFilename,debugSwitcher.getProfilesActive(),FileStoreTypeEnum.qiniu_server);

        String datePathFileNameWithOutSuffix=bo.getPathAndFileNameWithOutSuffix();
        String dotAndSuffix =bo.getDotAndSuffix();


        //原图
        String prefixBeforeDatePath = bo.getPrefixBeforeDateYYYY();
        String filePath =  datePathFileNameWithOutSuffix+dotAndSuffix;

        ImageInfoVo imageInfoVo = new ImageInfoVo();
        imageInfoVo.setBucket(bucket);
        imageInfoVo.setPrefixBeforeDatePath(prefixBeforeDatePath);
        imageInfoVo.setRelativePathAndFileName(filePath);
        imageInfoVo.setDotAndSuffix(dotAndSuffix);
        return imageInfoVo;
    }

    private FileModeEnum getFileModeEnum(String bucket){
        //公开和私有
        FileModeEnum fileMode= FileModeEnum.img_opened;
        if(QiNiuZonePrivateBucketEnum.BUCKET_CORP_CERT.getBucketCode().equals(bucket)|| QiNiuZonePrivateBucketEnum.PRIVATE_IMG.getBucketCode().equals(bucket)){
            fileMode = FileModeEnum.img_private;
        }
        return fileMode;
    }

    //校验大小和格式
    private long checkFileSizeAndSuffix(String bucket,MultipartFile multipartFile,String dotAndSuffix){

        //文件为空
        if (multipartFile.isEmpty()) {
            logger.warn("upload file is empty");
            throw new ServiceException(400,"上传文件为空");
        }

        long allow_max_size ;
        String allow_format;
        allow_max_size = FileFormatConstant.IMG_ALLOW_MAX_SIZE_IMG;
        allow_format = FileFormatConstant.IMG_ALLOW_FORMAT_IMG;

        long imageFileSize = multipartFile.getSize();
        //产品需求：
        if (imageFileSize > allow_max_size ) {
            logger.warn("upload images exceeded ImageSize ,imageFileSize {}", imageFileSize);
            DecimalFormat decimalFormat = new DecimalFormat("#.00");
            throw new ServiceException(400,"上传文件实际大小：" + decimalFormat.format(imageFileSize / 1024.0) + "KB，超过了默认大小："+decimalFormat.format(allow_max_size/1024 / 1024.0)+"M");
        }

        if (!allow_format.contains(dotAndSuffix)){
            logger.warn("upload images exceeded allow format ALLOW_FORMAT {}", allow_format);
            throw new ServiceException(400,"上传图片格式错误，允许的格式："+allow_format);

        }
        return imageFileSize;
    }

    /**
     * 文件上传到七牛云
     */
    public ImageInfoVo uploadFile(MultipartFile multipartFile, String bucket) {
        return uploadFile(multipartFile,bucket,StringUtils.EMPTY);
    }

    public ImageInfoVo uploadFile(MultipartFile multipartFile, String bucket,String oldExistPath) {
        logger.info("uploadFile enter  ,bucket {} ",bucket);
        if(StringUtils.isBlank(bucket)){
            throw new ServiceException(400,"upload file bucket can not null");
        }

        Map<String, BucketModel> bucketMap = selfDefineConfigUploadFile.getBucketMap();
        BucketModel bucketModel = bucketMap.get(bucket);
        if(bucketModel==null){
            logger.error("bucketModel is null ,bucket {},bucketMap {}",bucket,bucketMap);
            throw new ServiceException(400,"不合法的bucket");
        }

        ImageInfoVo imageInfoVo = new ImageInfoVo();
        imageInfoVo.setBucket(bucket);


        PathAndNameBo bo;
        if(StringUtils.isNotBlank(oldExistPath)){
            bo = FileUploadPathCreatorUtil.generateFilePathFromExist(bucket,getFileModeEnum(bucket),oldExistPath,multipartFile.getOriginalFilename(),debugSwitcher.getProfilesActive(),FileStoreTypeEnum.qiniu_server);
        }else{
            bo = FileUploadPathCreatorUtil.generateFilePathAndName(bucket,getFileModeEnum(bucket),multipartFile.getOriginalFilename(),debugSwitcher.getProfilesActive(),FileStoreTypeEnum.qiniu_server);
        }

        //校验大小和格式
        long imageFileSize = checkFileSizeAndSuffix(bucket,multipartFile,bo.getDotAndSuffix());


        //路径信息
        String datePathFileNameWithOutSuffix=bo.getPathAndFileNameWithOutSuffix();
        String dotAndSuffix =bo.getDotAndSuffix();

        String filePath =  datePathFileNameWithOutSuffix+dotAndSuffix;

        String prefixBeforeDatePath = bo.getPrefixBeforeDateYYYY();
        imageInfoVo.setPrefixBeforeDatePath(prefixBeforeDatePath);
        imageInfoVo.setRelativePathAndFileName(filePath);
        imageInfoVo.setDotAndSuffix(dotAndSuffix);


        /**
         *  1.检查图片是否需要cut，不需要裁剪直接上传file，
         *     新线程检查是否原图的bucket，上传file
         *
         *  2.需要cut，记录原图和裁剪后的字节数组
         *     分别上传
         *
         *  2.新线程 ，（1）除了认证图片的其它图片，把裁剪后的图片，在七牛在上传一份
         *             (2)认证图片，把原图上传到本地一份，命名方式为 xxx-original.jpg
         */
        try{
            boolean cutFlag =false;

            //获取原图 and 缩略图的字节内容
            byte[] byteData = null ;
            byte[] zoomByteData = null;

            //超过500K，再压缩
            if(bucketModel.isCutFlag()&&bucketModel.getWidth()!=null && bucketModel.getHeight() !=null && imageFileSize>500 * 1024 ){
                //文件字节数组，解决重复读取的问题
                byteData = FileUtils.coryMultipartFile2bytes(multipartFile) ;
                zoomByteData = ImageUtils.generateFixedSizeImage(byteData,bucketModel.getWidth(),bucketModel.getHeight());
                cutFlag = true;
            }

            ImageInfoVo qiNiulImageInfoVo = new  ImageInfoVo();
            qiNiulImageInfoVo.setPrefixBeforeDatePath(prefixBeforeDatePath);
            qiNiulImageInfoVo.setBucket(bucket);
            qiNiulImageInfoVo.setRelativePathAndFileName(imageInfoVo.getRelativePathAndFileName());

            if(cutFlag){
                sendToQiNiuByBytes(qiNiulImageInfoVo,zoomByteData);
            }else{
                //生成的图片 第一次上传到七牛
                sendToQiNiuByInputStream(qiNiulImageInfoVo,multipartFile.getInputStream());
            }

            final boolean CUT_FLAG_FINAL = cutFlag;
            final byte[] BYTE_DATA_FINAL = byteData;
            //新起一个thread执行另外一个上传
            threadPoolTaskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //认证图片的原图还需要执行两次上传
                        if(QiNiuZonePrivateBucketEnum.BUCKET_CORP_CERT.getBucketCode().equals(bucket)){
                            //3.认证图片的原图和缩略图都要传七牛
                            String originalRelativePath = bo.getOriginalPath();

                            ImageInfoVo qiNiulImageInfoVo = new  ImageInfoVo();
                            qiNiulImageInfoVo.setPrefixBeforeDatePath(prefixBeforeDatePath);
                            qiNiulImageInfoVo.setBucket(bucket);
                            qiNiulImageInfoVo.setRelativePathAndFileName(originalRelativePath);

                            if(CUT_FLAG_FINAL){
                                sendToQiNiuByBytes(qiNiulImageInfoVo,BYTE_DATA_FINAL);
                            }else{
                                //原图到七牛
                                sendToQiNiuByInputStream(qiNiulImageInfoVo,multipartFile.getInputStream());
                            }
                        }


                    } catch (Exception e) {
                        logger.error("file sender error {}", e);
                    }
                }
            });


        }catch (Exception e){
            logger.info("upload img error {}",e);
            throw new ServiceException(400,"上传文件错误");
        }

        //返回的是相对路径
        return imageInfoVo;
    }

    private void sendToQiNiuByBytes(ImageInfoVo qiNiulImageInfoVo, byte[] bytes){
        sendToQiNiu(qiNiulImageInfoVo,new FileSenderBO(FileSenderTypeEnum.bytes_array,bytes));
    }

    private void sendToQiNiuByInputStream(ImageInfoVo qiNiulImageInfoVo, InputStream inputStream){
        sendToQiNiu(qiNiulImageInfoVo,new FileSenderBO(FileSenderTypeEnum.input_stream,inputStream));
    }

    private void sendToQiNiu(ImageInfoVo qiNiulImageInfoVo, FileSenderBO fileSenderBO){
        try{
            //缩略图传七牛
            QiNiuStorageUtil.uploadFile(qiNiulImageInfoVo.getBucket(), qiNiulImageInfoVo,fileSenderBO);
        }catch (Exception e){
            logger.error("sendImg to qi niu error",e);
            throw new ServiceException(400,"图片上传异常");
        }


    }





    /****************************************************************
     * html 文件
     ***************************************************************/
    public ImageInfoVo addHtmlFile(  String htmlContent,boolean appendHtmlStartTagsFlag){
        return geneHtmlFile( htmlContent,appendHtmlStartTagsFlag,null);
    }

    public ImageInfoVo modifyHtmlFile(String htmlContent,boolean appendHtmlStartTagsFlag,String existRelativeUrl){
        String bucket = QiNiuZoneHtmlBucketEnum.HTML_FILE.getBucketCode();
        PathAndNameBo bo = FileUploadPathCreatorUtil.generateFilePathFromExist(bucket,getFileModeEnum(bucket),existRelativeUrl, DateUtil.nowMilliSecond()+".html",debugSwitcher.getProfilesActive(),FileStoreTypeEnum.qiniu_server);


        String datePathFileNameWithOutSuffix=bo.getPathAndFileNameWithOutSuffix();
        String dotAndSuffix =bo.getDotAndSuffix();

        String filePath =  datePathFileNameWithOutSuffix+dotAndSuffix;

        String prefixBeforeDatePath = bo.getPrefixBeforeDateYYYY();

        ImageInfoVo  imageInfoVo = new ImageInfoVo();
        imageInfoVo.setBucket(bucket);
        imageInfoVo.setPrefixBeforeDatePath(prefixBeforeDatePath);
        imageInfoVo.setRelativePathAndFileName(filePath);
        imageInfoVo.setDotAndSuffix(dotAndSuffix);
        return geneHtmlFile(htmlContent,appendHtmlStartTagsFlag,imageInfoVo);
    }

    private ImageInfoVo geneHtmlFile( String htmlContent,boolean appendHtmlStartTagsFlag,ImageInfoVo  imageInfoVo){
        if(StringUtils.isBlank(htmlContent)){
            return null;
        }
        String fileName = UUIDUtils.strongId()+".html";
        String bucket = QiNiuZoneHtmlBucketEnum.HTML_FILE.getBucketCode();
        if(imageInfoVo==null){
            imageInfoVo = this.createPathInfo(bucket,fileName);
        }

        //构造html文件
        StringBuffer sb=new StringBuffer();
        if(appendHtmlStartTagsFlag){
            sb.append(html_starting);
            sb.append(html_body_begin);
        }

        sb.append(htmlContent);

        if(appendHtmlStartTagsFlag){
            sb.append(html_body_end);
            sb.append(html_ending);
        }
        byte[] htmlByteData= sb.toString().getBytes(Charsets.UTF_8);

        ImageInfoVo qiNiulImageInfoVo = new  ImageInfoVo();
        qiNiulImageInfoVo.setPrefixBeforeDatePath(imageInfoVo.getPrefixBeforeDatePath());
        qiNiulImageInfoVo.setBucket(bucket);
        qiNiulImageInfoVo.setRelativePathAndFileName(imageInfoVo.getRelativePathAndFileName());
        sendToQiNiuByBytes(qiNiulImageInfoVo,htmlByteData);

        return imageInfoVo;
    }

    public String getHtmlContent(String absoluteUrlContent){
        if(StringUtils.isBlank(absoluteUrlContent)){
            logger.warn("getHtmlContent url is not correct ");
            return StringUtils.EMPTY;
        }

        if(!absoluteUrlContent.toLowerCase().startsWith("http://")&&!absoluteUrlContent.toLowerCase().startsWith("https://")){
            logger.warn("getHtmlContent url is not correct http ,url is {}",absoluteUrlContent);
            return absoluteUrlContent;
        }

        if(!absoluteUrlContent.contains("html")){
            logger.warn("getHtmlContent url is not correct bucket ,url is {}",absoluteUrlContent);
            return absoluteUrlContent;
        }

        StringBuffer fileStr=new StringBuffer();
        try{
            URL url = new URL(absoluteUrlContent);
            URLConnection conn = url.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String line ;
            while ((line = reader.readLine()) != null){
                fileStr.append(line);
            }
            reader.close();
        }catch (Exception e){
            logger.error("read file to string  error,url {}",absoluteUrlContent);
            return absoluteUrlContent;
        }
        String str = fileStr.toString();
        if(str!=null&&str.contains(html_body_begin)&&str.contains(html_body_end)){
            List<String> resultList = this.getStrings(str,Pattern.quote(html_body_begin)+"(.*?)"+Pattern.quote(html_body_end));
            return CollUtil.join(resultList," ");
        }else {
            return str;
        }

    }

    /**
     * 获取html中指定内容
     * @param input 文件所有内容
     * @param regex 正则表达式
     * @return
     */
    private List<String> getStrings(String input, String regex) {
        List<String> result = new LinkedList<String>();
        Pattern pat = Pattern.compile(regex, Pattern.DOTALL);
        Matcher mat = pat.matcher(input);
        while (mat.find()) {
            result.add(mat.group(1));
        }
        return result;
    }


}
